/**
 * Class responsible for handling the map/game board
 */
Map = new Class({
	
	Implements : [Options],
	
	/**
	 * @var Element
	 */
	unitContainer : null,
	
	/**
	 * Array to keep track of units on map, reflects board layout i.e. arr[x][y]
	 * @var array[x][y][UnitArr] 
	 */
	units : null,
	
	/**
	 * FLAT Array to keep track of units on map
	 * @var array[Unit] 
	 */
	unitsFlat : new Array(),
	
	/**
	 * @var Element
	 */
	tileContainer : null,
	
	/**
	 * Array to keep track of tiles, built to reflect board layout i.e. arr[x][y]
	 * @var Array
	 */
	tiles : null,
	
	/**
	 * @var Console
	 */
	console : null,
	
	options : {
		'tileWidthHeight'	: 70,
		'tileCountX'		: 12,
		'tileCountY'		: 12
	},
	
	/**
	 * Map Constructor
	 */
	initialize : function(options,console){
		this.setOptions(options);
		this.tileContainer = $('tiles');
		this.unitContainer = $('units');
		this.console = console;
		this.setMapSize();
		this.initTileGrid();
	},

	/**
	 * Set Map Size
	 */
	setMapSize : function(){
		this.tileContainer.setStyles({'width':(this.options.tileWidthHeight*this.options.tileCountX)+'px','height':(this.options.tileWidthHeight*this.options.tileCountY)+'px'});
	},
	
	/**
	 * Build the table of tiles and attach relevant events
	 */
	initTileGrid : function(){
		this.tiles = new Array(this.options.tileCountX);
		this.units = new Array(this.options.tileCountX);
		for(var x=0;x<this.options.tileCountX;x++){ // Store tiles as [x][y]
			this.tiles[x] = new Array(this.options.tileCountY);
			this.units[x] = new Array(this.options.tileCountY);
		}
		for(var y=0;y<this.options.tileCountY;y++){ // But render them [y][x] as theyre floated tiles
			for(var x=0;x<this.options.tileCountX;x++){
				this.tiles[x][y] = new Tile(x,y);
				this.units[x][y] = new Array();
				this.tileContainer.adopt(this.tiles[x][y]);
			}
		}
	},
	
	/**
	 * Add a unit to the unit container
	 * @param Unit
	 */
	addUnit : function(unit,x,y,r){
		this.unitContainer.adopt(unit.element);
		this.units[x][y].include(unit);
		this.unitsFlat.include(unit);
		unit.move(x,y,r,false,false);
		unit.setActionPoints(unit.getActionPointsLeft()+1); // correct for reduction in action points when setting up the map
	},
	
	/**
	 * Scan map and reveal hidden/just loaded units
	 * 
	 * @param LogItem
	 */
	scanBattlefield : function(){
		var obj = this;
		var run = false;
		var loadUnitsLog = this.console.logCommand('Scanning battlefield...').addEvent('complete',function(){
			if(!run){ // stop scan twice after complete event on log append
				for(x=0;x<obj.options.tileCountX;x++){
					for(var y=0;y<obj.options.tileCountY;y++){
						obj.scanTile.delay((y*100)+(x*20),obj,[obj.tiles[x][y]]);
					}
				}
				for(x=0;x<obj.options.tileCountX;x++){
					for(var y=0;y<obj.options.tileCountY;y++){
						obj.revealTile.delay((y*120)+(x*20),obj,[obj.tiles[x][y]]);
					}
				}
				run = true;
			}
		});
		loadUnitsLog.append.delay(1800,loadUnitsLog,[" complete."]);
	},
	
	scanTile : function(tile){
		tile.element.addClass('scan');
	},
	
	/**
	 * Reveal any hidden items in tile
	 * @param Tile tile
	 */
	revealTile : function(tile){
		tile.element.removeClass('scan');
		tile.getUnits().each(function(unit){
			unit.element.removeClass('invisible');
		});
	},
	
	/**
	 * Burst recursively from a point through the tile set in a particular direction i.e.
	 * xxxxxxx
	 *  xxxxx 
	 *   xxx
	 *    o
	 * @param int startX
	 * @param int startY
	 * @param string bearing 
	 * @param function lambda
	 */
	burstRecursive : function(startX,startY,bearing,lambda){
		var depth = 10; // @todo
		var breadth = 1;
		switch(bearing){
			case "N":
				for(var depthLvl=1;depthLvl<=depth;depthLvl++){
					if(this.tiles[startX] && this.tiles[startX][startY-depthLvl]){
						if(lambda.run([this.tiles[startX][startY-depthLvl]])===true) return; // first check straight from unit
					}
					for(var breadthLvl=1;breadthLvl<=breadth;breadthLvl++){
						if(this.tiles[startX+breadthLvl] && this.tiles[startX+breadthLvl][startY-depthLvl]){
							if(lambda.run([this.tiles[startX+breadthLvl][startY-depthLvl]])===true) return; // then check to each side of that
						}
						if(this.tiles[startX-breadthLvl] && this.tiles[startX-breadthLvl][startY-depthLvl]){
							if(lambda.run([this.tiles[startX-breadthLvl][startY-depthLvl]])===true) return;
						}
					}
					breadth++;
				}
				break;
				
			case "NE":
				for(var depthLvl=1;depthLvl<=depth;depthLvl++){
					var midX = startX+depthLvl;
					var midY = startY-depthLvl;
					if(this.tiles[midX] && this.tiles[midX][midY]){
						if(lambda.run([this.tiles[midX][midY]])===true) return; // first check straight from unit
					}
					for(var breadthLvl=1;breadthLvl<=breadth;breadthLvl++){
						if(this.tiles[midX-breadthLvl] && this.tiles[midX-breadthLvl][midY]){
							if(lambda.run([this.tiles[midX-breadthLvl][midY]])===true) return; // then check to each side of that
						}
						if(this.tiles[midX] && this.tiles[midX][midY+breadthLvl]){
							if(lambda.run([this.tiles[midX][midY+breadthLvl]])===true) return;
						}
					}
					breadth++;
				}
				break;
				
			case "E":
				for(var depthLvl=1;depthLvl<=depth;depthLvl++){
					if(this.tiles[startX+depthLvl] && this.tiles[startX+depthLvl][startY]){
						if(lambda.run([this.tiles[startX+depthLvl][startY]])===true) return; // first check straight from unit
					}
					for(var breadthLvl=1;breadthLvl<=breadth;breadthLvl++){
						if(this.tiles[startX+depthLvl] && this.tiles[startX+depthLvl][startY+breadthLvl]){
							if(lambda.run([this.tiles[startX+depthLvl][startY+breadthLvl]])===true) return; // then check to each side of that
						}
						if(this.tiles[startX+depthLvl] && this.tiles[startX+depthLvl][startY-breadthLvl]){
							if(lambda.run([this.tiles[startX+depthLvl][startY-breadthLvl]])===true) return;
						}
					}
					breadth++;
				}
				break;
				
			case "SE":
				for(var depthLvl=1;depthLvl<=depth;depthLvl++){
					var midX = startX+depthLvl;
					var midY = startY+depthLvl;
					if(this.tiles[midX] && this.tiles[midX][midY]){
						if(lambda.run([this.tiles[midX][midY]])===true) return; // first check straight from unit
					}
					for(var breadthLvl=1;breadthLvl<=breadth;breadthLvl++){
						if(this.tiles[midX] && this.tiles[midX][midY-breadthLvl]){
							if(lambda.run([this.tiles[midX][midY-breadthLvl]])===true) return; // then check to each side of that
						}
						if(this.tiles[midX-breadthLvl] && this.tiles[midX-breadthLvl][midY]){
							if(lambda.run([this.tiles[midX-breadthLvl][midY]])===true) return;
						}
					}
					breadth++;
				}
				break;
				
			case "S":
				for(var depthLvl=1;depthLvl<=depth;depthLvl++){
					if(this.tiles[startX] && this.tiles[startX][startY+depthLvl]){
						if(lambda.run([this.tiles[startX][startY+depthLvl]])===true) return; // first check straight from unit
					}
					for(var breadthLvl=1;breadthLvl<=breadth;breadthLvl++){
						if(this.tiles[startX+breadthLvl] && this.tiles[startX+breadthLvl][startY+depthLvl]){
							if(lambda.run([this.tiles[startX+breadthLvl][startY+depthLvl]])===true) return; // then check to each side of that
						}
						if(this.tiles[startX-breadthLvl] && this.tiles[startX-breadthLvl][startY+depthLvl]){
							if(lambda.run([this.tiles[startX-breadthLvl][startY+depthLvl]])===true) return;
						}
					}
					breadth++;
				}
				break;
			
			case "SW":
				for(var depthLvl=1;depthLvl<=depth;depthLvl++){
					var midX = startX-depthLvl;
					var midY = startY+depthLvl;
					if(this.tiles[midX] && this.tiles[midX][midY]){
						if(lambda.run([this.tiles[midX][midY]])===true) return; // first check straight from unit
					}
					for(var breadthLvl=1;breadthLvl<=breadth;breadthLvl++){
						if(this.tiles[midX] && this.tiles[midX][midY-breadthLvl]){
							if(lambda.run([this.tiles[midX][midY-breadthLvl]])===true) return; // then check to each side of that
						}
						if(this.tiles[midX+breadthLvl] && this.tiles[midX+breadthLvl][midY]){
							if(lambda.run([this.tiles[midX+breadthLvl][midY]])===true) return;
						}
					}
					breadth++;
				}
				break;
				
			case "W":
				for(var depthLvl=1;depthLvl<=depth;depthLvl++){
					if(this.tiles[startX-depthLvl] && this.tiles[startX-depthLvl][startY]){
						if(lambda.run([this.tiles[startX-depthLvl][startY]])===true) return; // first check straight from unit
					}
					for(var breadthLvl=1;breadthLvl<=breadth;breadthLvl++){
						if(this.tiles[startX-depthLvl] && this.tiles[startX-depthLvl][startY+breadthLvl]){
							if(lambda.run([this.tiles[startX-depthLvl][startY+breadthLvl]])===true) return; // then check to each side of that
						}
						if(this.tiles[startX-depthLvl] && this.tiles[startX-depthLvl][startY-breadthLvl]){
							if(lambda.run([this.tiles[startX-depthLvl][startY-breadthLvl]])===true) return;
						}
					}
					breadth++;
				}
				break;
				
			case "NW":
				for(var depthLvl=1;depthLvl<=depth;depthLvl++){
					var midX = startX-depthLvl;
					var midY = startY-depthLvl;
					if(this.tiles[midX] && this.tiles[midX][midY]){
						if(lambda.run([this.tiles[midX][midY]])===true) return; // first check straight from unit
					}
					for(var breadthLvl=1;breadthLvl<=breadth;breadthLvl++){
						if(this.tiles[midX] && this.tiles[midX][midY+breadthLvl]){
							if(lambda.run([this.tiles[midX][midY+breadthLvl]])===true) return; // then check to each side of that
						}
						if(this.tiles[midX+breadthLvl] && this.tiles[midX+breadthLvl][midY]){
							if(lambda.run([this.tiles[midX+breadthLvl][midY]])===true) return;
						}
					}
					breadth++;
				}
				break;
		}
		
	}
});

/* -- Map Class Static Methods -- */

/**
 * Used in conjuncton with ctrl+numpad keys
 */
Map.selectNearestUnit = function(tile){
	var units = tile.getUnits();
	g = GameEng.getInstance();
	if(units[0] && units[0].getPlayer()==g.hostPlayer){
		units[0].select();
		return true;
	}
}